/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
/*
 * bocciawii
 * Copyright (C) Grupo de trabajo ULE 2010 <ulewii@googlegroups.com>
 * 
 * bocciawii is free software: you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * bocciawii is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License along
 * with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef _ELEMENTO_ESTACIONARIO_H_
#define _ELEMENTO_ESTACIONARIO_H_
#include <iostream>
#include <GL/gl.h>
#include <GL/glu.h>
#include <SDL/SDL_image.h>
#include "obj-loader.h"
#include "rutas.h"
#include <cmath>

/// Modo de dibujado (bounding box)
#define DRAWING_BOXES 1
/// Modo de dibujado (objeto)
#define DRAWING_OBJETO 0

/**
 * @brief Base de los elementos 3D de los objetos
 *
 * Para objetos del entorno, y sirve como clase base para ElementoDinamico.
 * Son elementos de carga de objeto y texturas y posicionamiento (loc y rot).
 * Estos objetos podrán ser importados desde wavefronts .obj junto con la librería de materiales .mtl en el mismo directorio
 * (determinado por <code>rutas.h</code>)
 *
 * El motor únicamente admite ahora objs diseñados de tal manera que 
 * todos tienen un material al menos, no hay 'edges' (todas las caras son caras cerradas), las caras, como máximo, serán
 * quads, toda cara habrá de tener una normal asociada, y sólo un mapa de texturas (no hay multitexturing)
 *
 * Actualmente, las texturas se repiten si las coordenadas de texturas son > 1. El modo de textura es <code>GL_REPLACE</code>
 *
 * Con blender, estas exigencias son fáciles de cumplir, ya que el propio exporter
 * de al menos la versión 2.49, permite exportar las normales, no exportar los edges, y exportar los materiales.
 */
class ElementoEstacionario
{


public:

	/**
	 * @brief Constructor simple, inicializa variables
	 */
	ElementoEstacionario(int no_ocultar);	

	~ElementoEstacionario();


	/**
	 * @brief Carga un modelo desde un archivo fuente
	 *
	 * Carga el .obj dada una ruta
	 * @param ruta c++ string NOMBRE_DEL_FICHERO.OBJ
	 */
	void CargarObjeto(std::string ruta);	// Carga del .obj con el modelo

	/**
	 * @brief Carga un modelo desde otro <code>ElementoEstacionario</code>
	 *
	 * Copia de un elemento ya existente el modelo, texturas y display list.
	 * @param elemento Elemento original
	 */
	void CargarObjeto(ElementoEstacionario* elemento);	// Copia de un modelo

	/**
	 * @brief Setter para la posición del objeto
	 *
	 * Asigna la posición del elemento
	 * @param x El valor de la coordenada X
	 * @param y El valor de la coordenada Y
	 * @param z El valor de la coordenada Z
	 */
	void Mover(float x, float y, float z);

	/**
	 * @brief Setter para la posición del objeto
	 *
	 * Aumenta la posición del objeto dada una diferencia
	 * @param dx El valor a sumar a la posición X actual
	 * @param dy El valor a sumar a la posición Y actual
	 * @param dz El valor a sumar a la posición Z actual
	 */
	void MoverIncremental(float dx, float dy, float dz);

	/**
	 * @brief Setter para la rotación del objeto
	 *
	 * Asigna la rotación del objeto
	 * @param rx El ángulo que estará girado en X
	 * @param ry El ángulo que estará girado en Y
	 * @param rz El ángulo que estará girado en Z
	 */
	void Girar(float rx, float ry, float rz);

	/**
	 * @brief Setter para la rotación del objeto
	 *
	 * Asigna la rotación del objeto dada una diferencia con la anterior
	 * @param drx El ángulo a sumar a la rotación en X
	 * @param dry El ángulo a sumar a la rotación en Y
	 * @param drz El ángulo a sumar a la rotación en Z
	 */
	void GirarIncremental(float drx, float dry, float drz);



	/**
	 * @brief Getter para la transparencia del objeto
	 * 
	 * ¿Usará transparencia el objeto?
	 * @return <code>float</code> con la transparencia del objeto
	 */
	inline int EsTransparente()
	{ return es_transparente; }

	/**
	 * @brief Dibujado del elemento
	 *
	 * Dibujará el objeto con sus texturas y materiales en las posiciones indicadas
	 *  por las variables posición y rotación
	 */
	void Dibujate();

	/**
	 * @brief Getter para la posición
	 *
	 * Devolverá float* (float[3]) con la posición del objeto
	 * @return Puntero al array[3] de posición
	 */
	inline float* GetPosicion()
	{ return posicion; }
	
	/**
	 * @brief Getter para la rotación
	 *
	 * Devolverá float* (float[3]) con la rotación del objeto
	 * @return Puntero al array[3] de rotación
	 */
	inline float* GetRotacion()
	{ return rotacion; }
	
	/**
	 * @brief Getter para la visibilidad
	 *
	 * Es visible el objeto?
	 * @return 1 si es visible o <code>siempre_visible</code>, 0 sino
	 */
	inline int EsVisible()
	{   return es_visible; }

	/**
	 *@brief Determinar si el objeto es o no visible, para funciones de dibujado
	 *
	 *@param visibilidad 1 para dibujar, 0 para ocultarlo
	 */
	inline void SetVisibilidad(int visibilidad)
	{   es_visible = visibilidad;  }

	/**
	* Determinará si el objeto se encuentra en el punto de vista actual
	*
	* @param x anchura del viewport
	* @param y altura del viewport
	* @return <code>1</code> si se encuentra en el viewport, <code>0</code> de lo contrario
	*/
	int EnPuntoDeVista(float x, float y);

	/**
	 * @brief Setter para el modo de dibujado del objeto
	 *
	 * Establece el modo de dibujado del objeto (Testing purposes only :D)
	 * @param draw DRAWING_BOXES o DRAWING_OBJETO
	 */
	inline void DibujaCon (int draw)
	{   draw_mode = draw; }

	/**
	 * @brief Getter para la ID del objeto
	 * 
	 * Nos devolverá el string que contiene la id del objeto
	 * @return string con el identificador
	 */
	inline std::string GetID ()
	{ return id; }


protected:
	/// ID del objeto (coincide con el nombre del modelo cargado)
	std::string id;

	/// Vértices, normales, uv, etcétera del objeto
	objLoader malla;
	
	/// Nuestro vector de texturas OpenGL
	GLuint texturas[10]; 

	/// El centro de nuestro objeto
	float posicion[3]; 

	/// Ángulos de rotaciones del modelo
	float rotacion[3]; 

	/// 0 es totalmente opaco, 1 algo de transpariencia
	int es_transparente; 

	/// Alpha del objeto (0 transparente, 1 opaco)
	float transparencia; 

	/// 0 es invisible (no dibujar), 1 lo contrario
	int es_visible; 

	/// determinará si un objeto se dibuje siempre, estén sus bounds
	int siempre_visible;	

	/// <code>DRAWING_BOXES</code> ó <code>DRAWING_OBJETO</code>
	int draw_mode;			

	/// El identificador de GL de la display_list del objeto
	GLuint display_list;	

	/// La caja que lo contiene		
	float bounds[8][3];		

private:

	

	/**
	 * Cargará una textura para su uso en OpenGL a partir de una ruta
	 * @param ruta string con el nombre del fichero.png
	 * @param indice_tex indice para el vector de texturas donde guardar el índice de OpenGL
	 */
	void CargarTexturas(std::string ruta, int indice_tex);

	/**
	 * Crea el display list una vez la malla se ha cargado
	 *
	 */
	void CrearDisplayList();

	/**
	 * Auxiliar para la carga de texturas.
	 * Cargará una imagen .png en una superficie SDL
	 * @return SDL_Surface* con nuestra imagen
	 */
	SDL_Surface* LoadPNG(std::string ruta);

	/**
	 * Auxiliar para la carga de texturas.
	 * Dará la vuelta verticalmente a una array de píxeles
	 * @return 0 si todo va bien.
	 */
	int InvertImage(int pitch, int height, void* image_pixels);

	/**
	 * Auxiliar para la carga de texturas.
	 * Dará la vuelta verticalmente a una imagen para el correcto uso en OpenGL
	 * @return 0 si todo va bien.
	 */
	int InvertSurface(SDL_Surface* image);

	/**
	 * @brief Auxiliar para el dibujado
	 * Cargará el material que se le de para su uso en el dibujado del objeto
	 * @param material El material que se desea aplicar
	 */
	int AplicarMaterial(obj_material* material);

	/*
	 *  Determina la caja que contiene el objeto
	 */
	void CargarBounds();

	/*
	 *  Dibuja la caja del objeto
	 */
	void DibujarCaja();

};

#endif // _ELEMENTO_ESTACIONARIO_H_
